package cn.cyx.demo.chat.handler;

import cn.cyx.demo.chat.model.ChatMessage;
import cn.cyx.demo.chat.service.ChatService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.UUID;
import java.util.Map;

/**
 * WebSocket消息处理器 - 处理客户端WebSocket通信
 * 
 * 这个类是Netty WebSocket通信的核心处理器，负责：
 * 1. 用户连接管理：处理用户连接请求，建立用户会话
 * 2. 消息路由：根据消息类型分发到不同的处理方法
 * 3. 业务逻辑调用：调用ChatService处理具体的业务逻辑
 * 4. 响应发送：向客户端发送处理结果和状态信息
 * 
 * 消息类型支持：
 * - connect: 用户连接消息
 * - chat: 聊天消息
 * - join_room: 加入房间消息
 * - leave_room: 离开房间消息
 * 
 * 技术特点：
 * - 继承SimpleChannelInboundHandler，简化消息处理
 * - 使用Jackson进行JSON序列化/反序列化
 * - 支持Java 8时间类型序列化
 * - 完整的错误处理和日志记录
 * 
 * @author cyx
 * @since 1.0.0
 */
public class WebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    /**
     * 日志记录器
     * 用于记录WebSocket连接和消息处理的详细日志
     */
    private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);

    /**
     * 聊天服务，处理业务逻辑
     * 负责处理用户连接、消息广播等核心业务功能
     */
    private final ChatService chatService;

    /**
     * JSON对象映射器，用于序列化和反序列化JSON
     * 配置了JavaTimeModule以支持LocalDateTime序列化
     */
    private final ObjectMapper objectMapper;

    /**
     * 当前连接的用户ID
     * 每个WebSocket连接对应一个用户，存储用户唯一标识
     */
    private String userId;

    /**
     * 当前连接的用户名
     * 存储用户的显示名称，用于消息显示
     */
    private String username;

    /**
     * 构造函数
     * 
     * 初始化WebSocket处理器，包括：
     * 1. 注入聊天服务依赖
     * 2. 配置JSON序列化器，支持Java 8时间类型
     * 
     * @param chatService 聊天服务实例，用于处理业务逻辑
     */
    public WebSocketHandler(ChatService chatService) {
        this.chatService = chatService;

        // 初始化JSON对象映射器
        this.objectMapper = new ObjectMapper();
        // 注册JavaTimeModule以支持LocalDateTime序列化
        // 这样可以将Java 8的时间类型正确序列化为JSON
        this.objectMapper.registerModule(new JavaTimeModule());
    }

    /**
     * 处理WebSocket消息的核心方法
     * 
     * 这个方法会在收到WebSocket消息时被调用，是消息处理的入口点：
     * 1. 检查消息类型是否为文本帧（只处理文本消息）
     * 2. 提取消息内容
     * 3. 调用消息处理方法进行具体处理
     * 4. 处理异常并发送错误消息给客户端
     * 
     * 设计考虑：
     * - 只处理TextWebSocketFrame，忽略二进制消息
     * - 使用try-catch包装，确保异常不会导致连接断开
     * - 记录详细日志，便于问题排查
     * 
     * @param ctx   通道上下文，用于发送响应消息
     * @param frame WebSocket帧，包含客户端发送的消息
     * @throws Exception 处理异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        // 检查是否为文本消息帧
        if (frame instanceof TextWebSocketFrame) {
            // 提取文本消息内容
            TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
            String message = textFrame.text();

            try {
                // 解析并处理WebSocket消息
                handleWebSocketMessage(ctx, message);
            } catch (Exception e) {
                logger.error("处理WebSocket消息失败", e);
                sendErrorMessage(ctx, "消息处理失败: " + e.getMessage());
            }
        } else {
            // 忽略非文本消息（如二进制消息）
            logger.warn("收到非文本WebSocket帧: {}", frame.getClass().getSimpleName());
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("WebSocket连接建立: {}", ctx.channel().remoteAddress());
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (userId != null) {
            chatService.handleUserDisconnect(userId);
        }
        logger.info("WebSocket连接断开: {}", ctx.channel().remoteAddress());
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error("WebSocket连接异常", cause);
        ctx.close();
    }

    /**
     * 处理WebSocket消息
     * 
     * 根据消息类型分发到不同的处理方法：
     * 1. 解析JSON消息，提取消息类型
     * 2. 使用switch语句根据类型分发处理
     * 3. 对于未知消息类型，发送错误响应
     * 
     * @param ctx     通道上下文
     * @param message JSON格式的消息字符串
     * @throws Exception 处理异常
     */
    private void handleWebSocketMessage(ChannelHandlerContext ctx, String message) throws Exception {
        // 解析JSON消息
        JsonNode jsonNode = objectMapper.readTree(message);
        String type = jsonNode.get("type").asText();

        // 根据消息类型分发处理
        switch (type) {
            case "connect":
                handleConnect(ctx, jsonNode);
                break;
            case "chat":
                handleChat(ctx, jsonNode);
                break;
            case "join_room":
                handleJoinRoom(ctx, jsonNode);
                break;
            case "leave_room":
                handleLeaveRoom(ctx, jsonNode);
                break;
            default:
                logger.warn("未知的消息类型: {}", type);
                sendErrorMessage(ctx, "未知的消息类型: " + type);
        }
    }

    /**
     * 处理用户连接消息
     * 
     * 当用户首次连接时，这个方法会被调用：
     * 1. 验证用户名是否有效
     * 2. 生成唯一的用户ID
     * 3. 调用ChatService创建用户会话
     * 4. 发送连接成功响应给客户端
     * 5. 发送当前在线用户列表
     * 
     * @param ctx      通道上下文
     * @param jsonNode 解析后的JSON消息
     */
    private void handleConnect(ChannelHandlerContext ctx, JsonNode jsonNode) {
        // 提取并验证用户名
        String username = jsonNode.get("username").asText();
        if (username == null || username.trim().isEmpty()) {
            sendErrorMessage(ctx, "用户名不能为空");
            return;
        }

        // 生成唯一的用户ID
        this.userId = UUID.randomUUID().toString();
        this.username = username;

        // 调用ChatService创建用户会话
        chatService.handleUserConnect(userId, username, ctx.channel());

        // 发送连接成功响应给客户端
        sendSuccessMessage(ctx, "连接成功", Map.of(
                "userId", userId,
                "username", username));

        // 发送当前在线用户列表
        sendUserListUpdate(ctx);

        logger.info("用户连接成功: {} ({})", username, userId);
    }

    /**
     * 处理聊天消息
     */
    private void handleChat(ChannelHandlerContext ctx, JsonNode jsonNode) {
        if (userId == null) {
            sendErrorMessage(ctx, "请先连接");
            return;
        }

        String content = jsonNode.get("content").asText();
        if (content == null || content.trim().isEmpty()) {
            sendErrorMessage(ctx, "消息内容不能为空");
            return;
        }

        chatService.handleChatMessage(userId, content);
    }

    /**
     * 处理加入房间消息
     */
    private void handleJoinRoom(ChannelHandlerContext ctx, JsonNode jsonNode) {
        if (userId == null) {
            sendErrorMessage(ctx, "请先连接");
            return;
        }

        String roomId = jsonNode.get("roomId").asText();
        if (roomId == null || roomId.trim().isEmpty()) {
            sendErrorMessage(ctx, "房间ID不能为空");
            return;
        }

        boolean success = chatService.joinRoom(userId, roomId);
        if (success) {
            sendSuccessMessage(ctx, "成功加入房间", Map.of("roomId", roomId));
        } else {
            sendErrorMessage(ctx, "加入房间失败");
        }
    }

    /**
     * 处理离开房间消息
     */
    private void handleLeaveRoom(ChannelHandlerContext ctx, JsonNode jsonNode) {
        if (userId == null) {
            sendErrorMessage(ctx, "请先连接");
            return;
        }

        String roomId = jsonNode.get("roomId").asText();
        if (roomId != null) {
            chatService.leaveRoom(userId, roomId);
            sendSuccessMessage(ctx, "成功离开房间", Map.of("roomId", roomId));
        }
    }

    /**
     * 发送成功消息
     */
    private void sendSuccessMessage(ChannelHandlerContext ctx, String message, Map<String, Object> data) {
        try {
            Map<String, Object> response = Map.of(
                    "type", "success",
                    "message", message,
                    "data", data);
            String jsonResponse = objectMapper.writeValueAsString(response);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(jsonResponse));
        } catch (Exception e) {
            logger.error("发送成功消息失败", e);
        }
    }

    /**
     * 发送错误消息
     */
    private void sendErrorMessage(ChannelHandlerContext ctx, String message) {
        try {
            Map<String, Object> response = Map.of(
                    "type", "error",
                    "message", message);
            String jsonResponse = objectMapper.writeValueAsString(response);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(jsonResponse));
        } catch (Exception e) {
            logger.error("发送错误消息失败", e);
        }
    }

    /**
     * 发送用户列表更新消息
     */
    private void sendUserListUpdate(ChannelHandlerContext ctx) {
        try {
            java.util.List<String> onlineUsers = chatService.getOnlineUsers();
            Map<String, Object> response = Map.of(
                    "type", "user_list",
                    "users", onlineUsers);
            String jsonResponse = objectMapper.writeValueAsString(response);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(jsonResponse));
        } catch (Exception e) {
            logger.error("发送用户列表更新失败", e);
        }
    }
}